﻿using System;
using UnityEngine;

namespace Nirvana
{
    /// <summary>
    /// The Quality config for a quality level.
    /// </summary>
    [Serializable]
    public sealed class QualityLevel
    {
        /// <summary>
        /// 质量级别的名称
        /// </summary>
        [SerializeField]
        private string name;
        /// <summary>
        /// 前向渲染使用的像素灯的最大数量
        /// </summary>
        [SerializeField]
        [Tooltip("Number of pixel lights to use.")]
        private int pixelLightCount = 1;

        /// <summary>
        /// 你可以设置使用最大分辨率的纹理或者部分纹理（低分辨率纹理的处理开销低）。
        /// 选项有 完整分辨率，1/2分辨率，1/4分辨率，1/8分辨率
        /// </summary>
        [SerializeField]
        [Tooltip("Texture quality level.")]
        [EnumInt("FullRes", 0)]
        [EnumInt("Half Res", 1)]
        [EnumInt("Quarter Res", 2)]
        [EnumInt("Eighth Res", 3)]
        private int textureQuality = 0;

        /// <summary>
        /// 全局各向异性过滤模式，纹理的各向异性等级（当相机视野与场景的角度很小时，
        /// 调整纹理的精度，各向异性等级越大，纹理精度越大）。
        /// Per Texture 每纹理, 每张纹理都将使用各向异性渲染。
        /// </summary>
        [SerializeField]
        private AnisotropicFiltering anisotropicTextures = AnisotropicFiltering.Enable;

        /// <summary>
        /// 设置抗锯齿级别。选项有​​2倍，4倍和8倍采样。
        /// </summary>
        [SerializeField]
        [Tooltip("Screen anti aliasing.")]
        [EnumInt("Disabled", 0)]
        [EnumInt("2x Multi Sampling", 2)]
        [EnumInt("4x Multi Sampling", 4)]
        [EnumInt("8x Multi Sampling", 8)]
        private int antiAliasing = 0;

        /// <summary>
        /// 是否使用粒子软融合。
        /// </summary>
        [SerializeField]
        [Tooltip("Use soft blending for particles?")]
        private bool softParticles = false;

        /// <summary>
        /// 软植被,用于地形引擎中的植被
        /// 如果启用，植被将有平滑的边缘，如果禁用所有植物将有硬边但是渲染会快两倍
        /// 若想要在一些外部条件得到满足时某pass才渲染，就可以通过使用RequireOptions标签，它的值是一个空格分割的字符串，
        /// 目前由Unity3d支持的选项只有一个，就是shader渲染植被之时： { " RequireOptions" =" SoftVegetation"}
        /// </summary>
        [SerializeField]
        [Tooltip("Use soft blending for vegetation when using terrain?")]
        private bool softVegetation = false;

        /// <summary>
        /// 设置游戏时是否实时更新反射探针
        /// </summary>
        [SerializeField]
        [Tooltip("Allow real-time rendering of Reflection Probes?")]
        private bool realtimeReflectionProbes = false;

        /// <summary>
        /// 渲染时强制广告牌面向摄像机以代替摄像机平面。会有更好的效果，但是消耗也更高。
        /// </summary>
        [SerializeField]
        [Tooltip("Make billboards face towards camera position. Otherwise they face towards camera plane. This makes billboards look nicer when camera rotates but is more expensive to render.")]
        private bool billboardsFaceCameraPosition = true;

        /// <summary>
        /// 当使用阴影蒙版混合光照模式时，选择阴影蒙版的行为。通过光照窗口设置。
        /// Distance Shadowmask:在阴影距离内使用实时阴影，超出则用烘焙阴影。
        /// Shadowmask:静态物体总是投射烘焙阴影。
        /// </summary>
        [SerializeField]
        private ShadowmaskMode shadowmaskMode = ShadowmaskMode.Shadowmask;
        /// <summary>
        /// 选择使用的阴影类型。可用选项包括软硬阴影，仅硬阴影，禁用阴影。
        /// </summary>
        [SerializeField]
        [Tooltip("Shadow quality.")]
        private ShadowQuality shadows = ShadowQuality.All;

        /// <summary>
        /// 选择渲染阴影的分辨率。可用选项包括低分辨率，中分辨率，高分辨，非常高分辨率。越高的分辨率，处理开销越大。
        /// </summary>
        [SerializeField]
        [Tooltip("Shadow resolution.")]
        private ShadowResolution shadowResolution = ShadowResolution.VeryHigh;

        /// <summary>
        /// 选择平行光的投射阴影的方法。
        /// Close Fit:渲染更高分辨率的阴影但有时候镜头移动时阴影会有些许的摇晃。
        /// Stable Fit:低分辨率但不会因镜头移动而摇晃。
        /// </summary>
        [SerializeField]
        [Tooltip("Shadow projection.")]
        private ShadowProjection shadowProjection = ShadowProjection.CloseFit;

        /// <summary>
        /// 输入最大的阴影可视距离（从摄像机开始）。Unity不会渲染超出此距离的阴影。
        /// </summary>
        [SerializeField]
        [Tooltip("Shadow distance.")]
        private float shadowDistance = 20;

        /// <summary>
        /// 输入这个值（阴影近平面偏移）会引起巨大三角形被shadow pancaking（阴影平坠是解决阴影暗斑的一个方案）扭曲的问题
        /// </summary>
        [SerializeField]
        [Tooltip("Offset shadow near panel too account for large triangles being distorted by shadow pancaking.")]
        private float shadowNearPlaneOffset = 2;

        /// <summary>
        /// 选择使用的阴影级联数量。可用选项包括无，2，4。更高的数量的级联有更好的效果，
        /// 但也会有更高的开销。（参考平行光阴影以获取更多的细节）
        /// </summary>
        [SerializeField]
        [Tooltip("Number of cascades for directional light shadows.")]
        [EnumInt("No Cascades", 0)]
        [EnumInt("Two Cascades", 2)]
        [EnumInt("Four Cascades", 4)]
        private int shadowCascades = 0;

        [SerializeField]
        private float shadowCascade2Split = 0.33f;

        [SerializeField]
        private Vector3 shadowCascade4Split = new Vector3(0.067f, 0.2f, 0.467f);

        /// <summary>
        /// 选择动画期间能影响顶点的骨骼数量。可用选项1，2，4，无限制。
        /// </summary>
        [SerializeField]
        [Tooltip("Bone count for mesh skinning.")]


#if UNITY_2019_1_OR_NEWER
        private SkinWeights blendWeights = SkinWeights.FourBones;
#else
        private BlendWeights blendWeights = BlendWeights.FourBones;
#endif

        /// <summary>
        /// 选择渲染与垂直空白同步或者不同步。unity可以与显示设备的刷新频率同步渲染以避免画面撕裂。
        /// 可用选项有每个垂直空白，每两个垂直空白，每秒的垂直空白和不同步。
        /// 手游平台，不受此控制，以 Application.targetFrameRate值为主。
        /// </summary>
        [SerializeField]
        [Tooltip("Limit refresh rate to avoid tearing.")]
        [EnumInt("Don't Sync", 0)]
        [EnumInt("Every V Blank", 1)]
        [EnumInt("Every Second V Black", 2)]
        private int vSyncCount = 0;

        /// <summary>
        /// 设置LOD细节等级偏好。
        /// 细节等级取决于物体在屏幕上的尺寸。当其尺寸处于两个等级之间时，可以偏向于选择更少或更多细节的可用模型。
        /// 可以为设置0到正无穷之间的分数。0-1为偏爱更少细节，大于1偏爱更好的细节
        /// </summary>
        [SerializeField]
        private float lodBias = 2;
        /// <summary>
        /// 设置最高的细节等级。（低于最高LOD的模型将不会加载，0是质量最高的模型，数字越大质量越低。
        /// 比如设置为1，LOD Group中只有1以及更大的LOD会加载显示）
        /// </summary>
        [SerializeField]
        private int maximumLODLevel = 0;

        /// <summary>
        /// 设置用来估算粒子系统碰撞的最大射线投射数量。
        /// </summary>
        [SerializeField]
        [Tooltip("Number of rays to cast for approximate world collisions.")]
        private int particleRaycastBudget = 4096;

        /// <summary>
        /// 图形驱动程序排队的最大帧数
        /// 图形驱动程序可以对要渲染的帧进行队列处理。当 CPU 的工作明显少于显卡时， 该队列可能会变得相当大
        /// 在 PC 上，默认值为 2， 可在帧延迟和帧率之间达到最佳平衡
        /// 注意，可以将 maxQueuedFrames 的值设置为较小的值以减少输入延迟，因为此时 CPU 将等待显卡完成前一帧的渲染。但这样做有一个缺点 - 可能导致帧率下降。
        /// 目前，仅在 Direct3D 9、Direct3D 11 和 NVN 图形 API 中实现了 maxQueuedFrames；其他图形 API 会将其忽略。
        /// </summary>
        [SerializeField]
        private int maxQueuedFrames = 2;

        /// <summary>
        /// 设置cpu每帧把缓存纹理传输到gpu的时间单位为毫秒。最小值是1，最大值是33。
        /// </summary>
        [SerializeField]
        [Tooltip("Async Upload TimeSlice in Milliseconds.")]
        private int asyncUploadTimeSlice = 2;
        /// <summary>
        /// 使用asyncUploadBufferSize设置异步纹理上传的缓冲区大小。大小以兆字节为单位。
        /// 最小值为2，最大值为512.虽然缓冲区会自动调整大小以适合当前正在加载的最大纹理，
        /// 但建议将该值设置为场景中最大纹理的大小，以避免重新调整缓冲区的大小会产生性能成本
        /// </summary>
        [SerializeField]
        [Tooltip("Async Upload Ring Buffer Size in MB.")]
        private int asyncUploadBufferSize = 4;


        [SerializeField]
        [Tooltip("画面帧率.")]
        private int frameRate = 0;

        [SerializeField]
        [Tooltip("设置不同品质的降频分辨率.")]
        private bool setResolution = false;
        [SerializeField]
        [Tooltip("分辨率宽度.")]
        private int resulutionWidth = 1280;

        [SerializeField]
        [Tooltip("分辨率高度.")]
        private int resulutionHeight = 720;

        [SerializeField]
        [Tooltip("屏幕变暗时间.")]
        private int sleepTimeout = 0;

        private int overrideShadowQuality = -1;


        public string Name
        {
            get { return this.name; }
        }

        public int ResulutionWidth { get { return resulutionWidth; } }
        public int ResulutionHeight { get { return resulutionHeight; } }


        public ShadowQuality OverrideShadows
        {
            set { overrideShadowQuality = (int)value; }
        }

        public float ShadowDistance
        {
            get
            {
                return this.shadowDistance;
            }
            set
            {
                if (this.shadowDistance != value)
                {
                    this.shadowDistance = value;
                    QualitySettings.shadowDistance = this.shadowDistance;
                }
            }
        }

        /// <summary>
        /// Active this quality config.
        /// </summary>
        public void Active()
        {
            // Rendering
            QualitySettings.pixelLightCount = this.pixelLightCount;
            QualitySettings.globalTextureMipmapLimit = this.textureQuality;
            QualitySettings.anisotropicFiltering = this.anisotropicTextures;
            QualitySettings.antiAliasing = this.antiAliasing;
            QualitySettings.softParticles = this.softParticles;
            QualitySettings.softVegetation = this.softVegetation;
            QualitySettings.realtimeReflectionProbes = this.realtimeReflectionProbes;
            QualitySettings.billboardsFaceCameraPosition = this.billboardsFaceCameraPosition;

            // Shadows
            QualitySettings.shadows = -1 != overrideShadowQuality ? (ShadowQuality)this.overrideShadowQuality : this.shadows;

            QualitySettings.shadowResolution = this.shadowResolution;
            QualitySettings.shadowProjection = this.shadowProjection;
            QualitySettings.shadowDistance = this.shadowDistance;
            QualitySettings.shadowNearPlaneOffset = this.shadowNearPlaneOffset;
            QualitySettings.shadowCascades = this.shadowCascades;
            QualitySettings.shadowCascade2Split = this.shadowCascade2Split;
            QualitySettings.shadowCascade4Split = this.shadowCascade4Split;

            // Other
#if UNITY_2019_1_OR_NEWER
            QualitySettings.skinWeights = blendWeights; 
#else
             QualitySettings.blendWeights = this.blendWeights;
#endif

            QualitySettings.vSyncCount = this.vSyncCount;
            QualitySettings.lodBias = this.lodBias;
            QualitySettings.maximumLODLevel = this.maximumLODLevel;
            QualitySettings.particleRaycastBudget = this.particleRaycastBudget;
            QualitySettings.maxQueuedFrames = this.maxQueuedFrames;
            QualitySettings.asyncUploadBufferSize = this.asyncUploadBufferSize;
            QualitySettings.asyncUploadTimeSlice = this.asyncUploadTimeSlice;

            if (this.frameRate > 0) Application.targetFrameRate = this.frameRate;
            if (this.sleepTimeout > 0) Screen.sleepTimeout = this.sleepTimeout;

            if (setResolution && resulutionWidth > 800 && resulutionHeight > 640)
            {
                Screen.SetResolution(resulutionWidth, resulutionHeight, true);
            }
        }
    }
}

